Room Extension Developer Guide

Table of Contents

Introduction

How to develop an Extension?

Configure the Extension

Include the extension API

Make the extension displayable

List the extension points

List the input and output parameters

Make the extension executable

How to develop a Value Set Provider?

List the value sets

List the required parameters

Make the value set provider executable

Extension Points and Context

List of Extensions provided by SAP

FAQ

Glossary


Introduction

In SAP Collaboration Room 5.0, special iViews containing initialisation or deletion code were executed at room creation or room deletion. This way, the application developer was enabled to react on the events room creation and room deletion.

In SAP Collaboration Room 6.0 SP2 Patch 5 an enhanced concept called Room Extension was introduced. During their lifetime, the room and the room parts pass through a defined set of extension points like ON_CREATE_ROOM, ON_DELETE_ROOM or ON_ADD_ROOMPART_TO_ROOM. Extension points are steps in the life cycle of a room, where customer code can be plugged in and executed. The customer provides this code as an extension.

An extension is a portal service, that defines a set of input parameters it needs to work, and a set of output parameters it returns as result. It also implements methods to compute the result. There are two times in using the extension service:

The name Extension may suggest that it would be something optional that adds value to the room. But that does not score a bull's eye. As extensions define the repositories for iViews in rooms and room parts they are mandatory constituent parts of the template definitions. Extensions can also be used for other purposes, like for example to connect to backend systems, to set permissions or to provide or concatenate information. To find out what extensions can do, it may help to look at some existing extensions. Chapter 5 lists extensions provided by SAP.

This instruction does not deal with details how the room handles extensions internally; it focuses on how to develop an extension. The developer should know how to develop a portal service, how to create rooms and how to create room templates. However, precisely performing the steps in this instruction can also lead to first results.

Room Extension Sample Project

For a sample project that can be used as a template for own extension projects, see Guide to Examples

How to develop an Extension?

First step to develop an extension is to create a new Java portal project, including a Java class for the extension, e.g. SampleExtension.java. Next is to configure the project in the file portalapp.xml and in the extension registry. Then the extension API is included. The extension is provided with a resource bundle to make it displayable. The extension points and the input- and output parameters are listed and at last the methods validate, process, rollback and commit are added to make the extension executable.

Configure the Extension

Because the extension is a portal service, a service entry in the portalapp.xml file of the project is required. The service name can be chosen freely as long as it fulfils the Java naming conventions. For at least one service in a portalapp.xml file the generic_classloader_registration must be activated, and there must be a generic service key defined which can be a GUID, the class name or anything else that is unique. It is sufficient to do this for one service.

<service name="SampleExtensionService">
     <service-config>
          <property name="className" value="com.sample.SampleExtension"/>
          <property name="startup" value="true"/>
     </service-config>
     <service-profile>
          <property name="generic_classloader_registration" value="yes"/>
          <property name="generic_service_key" value="someSampleGuid"/>
     </service-profile>
</service>

The development project containing the extension must also deploy a Configuration Framework based configurable to the extension registry, which is located in the configuration path Collaboration -> Extension -> Registry. In this configurable, the extension id is defined. A common choice is to take the name of the class for the id. The property service is a combination of the par file name of your project plus the service name defined in the file portalapp.xml. Please note that the par file name and the service name may differ from the package name and the class name. It is a common mistake to use the package name and the class name here.

<Configurable configclass="Extension">
     <property name="id"           value="SampleExtension"/>
     <property name="service"      value="com.sample.SampleExtensionService"/>
     <property name="category"     value="room"/>
     <property name="active"       value="true"/>
</Configurable>

The configurable additionally defines a category, which must be "room" for room related extensions or "common" for unspecific extensions, that are used in the room. An extension is room related, if it accesses the Room API. The Concat extension e.g. is not room related, because it just concatenates two strings and does not access the Room API.

There is also an active flag in the configurable that is used to disable the extension. Disabling should be handled with care, because it corrupts templates using this extension. Early versions of the room do not validate the templates, so that there will be exceptions if they are used. Later versions (EP 6.0 SP10 or higher) validate the templates so that they are no longer used.

Include the extension API

The extension API is enclosed in the file coll.shared.extension_api.jar, which must be included into a new project. This file is located in an installed portal (EP 6.0 SP2 Patch 5 or higher).

To include the API in SAP NetWeaver Developer Studio or Eclipse, first define two Classpath Variables in Window -> Preferences -> Java -> Classpath Variables:

WEBAS_HOME = <hd>\usr\sap\<instance name>\JC<instance no>\j2ee\cluster\server<no>
PORTAL_HOME = WEBAS_HOME\apps\sap.com\irj\servlet_jsp\irj\root\WEB-INF\portal

Go to your project, Properties -> Java Build Path -> Libraries and add the variables:

PORTAL_HOME/portalapps/com.sap.netweaver.kmc.util/lib/kmc.util.core_api.jar
PORTAL_HOME/portalapps/com.sap.netweaver.coll.shared/lib/coll.shared.extension_api.jar
PORTAL_HOME/portalapps/com.sap.netweaver.coll.shared/lib/coll.shared.roomobject_api.jar

To access the API, a service reference in the portalapp.xml file of the project is also mandatory:

<application-config>
     ...
     <property name="ServicesReference"  
     value="..., com.sap.netweaver.coll.shared, ..."/>
     ...
</application-config>

An extension is a portal service and therefore extends the class GenericService.To implement the extension, an IExtensionBuilderFactoryprovides specific objects like IParameterInfoand IExtensionResult. In the extension class, this factory is retrieved from the extension runtime:

import com.sap.netweaver.coll.shared.api.extension.IExtensionBuilderFactory;
  
public class SampleExtension extends GenericService implements IExtension 
{
     private IExtensionBuilderFactory extensionFactory = 
     (IExtensionBuilderFactory)ExtensionRuntime.getFactory();
     ...

To get more information about extension objects, methods and parameters consult the Java Documentation of the Interfaces.

Make the extension displayable

Because an extension is displayed in the template creation wizard, it needs a resource bundle to translate name and description into the required language.

private static final String BUNDLE = "com.sample.bundles.SampleBundle"; 
private static final String ID = "sampleExtension"; 

public String getName(Locale locale) {
     return ResourceBundles.getBundle(BUNDLE).getString("lbl_" + ID, locale);
}
public String getDescription(Locale locale) {
     return ResourceBundles.getBundle(BUNDLE).getString("dsc_" + ID, locale);
}

The bundle is located in the same project as the extension. The entries in the bundle file look like this:

lbl_sampleExtension=Sample Extension
dsc_sampleExtension=Sample with explanations how to create an extension.

List the extension points

Extension points are steps in the life cycle of a room, where the extension is executed. There is no need for an extension to react on each extension point of a room; it depends on the purpose of the extension, which extension points are useful. For Collaboration Room, only the points provided in the class RoomExtensionPoint from the Room API can be used (see 4). The extension provides a list of points it can handle.

public IExtensionPoint[] getExtensionPoints() {
     IExtensionPoint[] result = {
          RoomExtensionPoint.ON_CREATE_ROOM,
          RoomExtensionPoint.ON_DELETE_ROOM,
          //...
     };
     return result;
}

In the template wizard, this list of extension points is exposed to the template creator, who uses it to define the parameter mappings. The extension developer should provide documentation to give the template creator an understanding of the extension and its best use.


Figure 1: Extension Point List of an Extension in the Template Wizard

List the input and output parameters

An extension also defines a list of input- and output parameters. For example, an extension that creates a folder may require a parent path and output a folder id. The parameter list depends on the extension point: if the mentioned folder is created in ON_CREATE_ROOM, the parameters are listed only for that point.

For the input parameters of an extension only available parameters can be used. The available parameters divide into two different kinds:

The sample code shows how the input parameters are defined: The extension uses the extension factory to build up a list of IParameterInfo. All required input parameters must be listed, because only the listed parameters will be available for processing.

public IParameterInfo[] getInputParameterInfos(IExtensionPoint extensionPoint) 
{
     //ON_CREATE_ROOM
     if (RoomExtensionPoint.ON_CREATE_ROOM.equals(extensionPoint)) {

          IDisplayable parameter1Displayable = extensionFactory.createDisplayable(
                 PARAMETER1_ID, RESOURCE_BUNDLE, "lbl_" + PARAMETER1_ID, 
                 "dsc_" + PARAMETER1_ID);

          IParameterInfo[] result = { 
                 extensionFactory.createInputParameterInfo(  parameter1Displayable, 
                          PARAMETER1.CLASS, VALUE_SET_PROVIDER_ID, optional),
                 extensionFactory.createSystemParameterInfo(PARAMETER2_ID, 
                          PARAMETER2_CLASS, mandatory),
                 //...
          };
          return result;
     }

     //ON_DELETE_ROOM
     //...

     return null;
}

To build up the IParameterInfo for a system parameter requires:

Because template parameters are used in the template, additional information is required:


Figure 2: Parameter Mapping in the Template Wizard

The set of available parameters in the template wizard encloses constants, external parameters and output parameters of extensions. While the mapping of a constant directly assigns the value, the mapping of external parameters or extension output generates a reference to a variable that is filled at room or room part creation.

Constants are fixed by the template creator at design time. For example the parameter Access in Figure 2 can be private or public. These values are offered in a drop down list box that is filled by a value set provider. Usually the extension developer knows, which input the extension can compute, and therefore will also develop the value set provider.

External parameters are defined at design time, but the value is filled in manually at run time. The user who creates a room is prompted for the value.

Extension output are parameters filled by extension results. To use output parameters requires an overview over the whole template, because the availability of these parameters depends on the extension points at which they are computed. An extension can even use its own output as input for a later extension point. But attention: expecting an output as input for an earlier extension point necessarily causes an error. Obviously, for the template creator it is possible to configure deadlocks (see Figure 3). If e.g. extension e1 requires parameter p2 from extension e2, but extension e2 requires parameter p1 from extension e1 at the same extension point, this cannot work.


Figure 3: Direct and timed Deadlock

Unfortunately a deadlock causes no exception at template creation, the error occurs at room- or room part creation. For the extension developer as well as for the template creator therefore it is important to plan and test the extension usage.

Figure 4

Parameter Mapping in a Room- or Room Part Template

Figure 4 shows how the parameter mapping can be planned. External parameters, system parameters, output parameters and constants are used as input parameters of extensions at a dedicated extension point. Extension 1 uses its output at point 1 as input at point 2. Extension 2 shows that extensions do not need to have input parameters. For example an extension could produce a GUID that is later used to create a unique object. Extensions also do not need to have output parameters. An extension may just report the input, like the status engine does. Extensions even do not need to have any parameters, there could be an extension that just counts how many rooms are created and deleted and reports the value to some backend.


Figure 5: Parameter Mapping Sample

Figure 5 gives a real life example for a parameter-mapping plan. The template creator wants the room to create a document folder that should be named "Documents of %s". At "%s" the creator wants to have the name of the room the folder belongs to. The creator uses the RoomPropertyExtension to extract the room name from the system parameter RoomInfo. The Concat extension allows concatenating the room name and the string constant "Workspace of ". The result is given to the CmRoomExtension, which creates the workspace and outputs the path that is mapped to the start_uri of an iView. All assignments are performed at the same extension point. The extension engine automatically resolves the dependencies and executes the extensions in the right order.

Make the extension executable

When a room or a room part is created, the constant values as well as the parameter references are copied from the template to the room or room part context. During the lifetime of the room, the values and references are taken from the context and exposed to the extension, which processes the input and returns the result. The room or room part adds the result to the context, so that it is available when another extension point is processed.

Figure 6: Dataflow of an extension at a dedicated extension point

While an extension point is processed, lots of extensions may be executed. The problem arises that if some of them succeed, but others fail, this may cause an inconsistent context of the room or the room part. For example, a workspace for a room part may have been created by extension 1, but extension 2 fails in setting the permissions. Obviously, a mechanism to rollback the extensions would be helpful. Furthermore, if it is known in advance that an extension cannot process, e.g. because a required resource or a configurable is missing, it saves performance to immediately throw an exception instead of starting to process the extensions, then running into the error and rolling everything back.

Figure 7

: How Extensions are executed

Extensions implement methods to approach the described transactional behaviour. When an extension point is processed, first the validate method of all extensions is called. If one extension fails, the processing of the extension point terminates on error. If all extensions can validate, the process method is called in a second step. If one extension fails to process, all extensions are rolled back, otherwise all commit.

In the validate method, the extension takes the input parameters from the context and throws an exception if they are not consistent. Nothing should be processed. Since the input parameters may come from the output of other extensions, which also do not process in this step, they may be missing. The extension therefore should treat all parameters as being optional and validate only the parameters that are present. The validation also may enclose to check the availability of a used backend or a required configurable. If an extension does not implement the validate method, it may still work, but cause a high performance effort, because avoidable errors must be rolled back over all extensions involved in an extension point.

In the process method, the extension takes the input parameters from the context, processes them and returns the output parameters as the result. Everything processed in this method must be reversible, e.g. if the extension deletes something, it should copy it to a recycler or create a backup. There is the special case that an extension creates something, e.g. a workspace, and must be enabled to delete it when it is asked to rollback. Since the extension is a singleton, it does not have instance properties were the workspace could be stored for later access. It is also not stored to the context before the extension commits. So how can it be accessed for the roll back? - The extension fills the parameter directly to the context and removes it in the rollback method (see sample code).

The rollback method is provided with the same context as the process method, but it may be enriched with objects created during process. The task of the rollback is to undo everything the process method did. Created objects are deleted, and deleted objects are restored, recycled or recreated.

The commit method finalizes the execution of the extension. It is not allowed to do any processing here; the extension must have fulfilled its purpose, even if commit would not be called. Commit just removes backups provided for the rollback that are no longer useful.

public IExtensionResult process(IExtensionPoint point, IExtensionContext context) throws ExtensionException
{
     //ON_CREATE_ROOM
     if (RoomExtensionPoint.ON_CREATE_ROOM.equals(point)) {
          //get parameters
          String parameterValue1 = (String)context.getValue(PARAMETER1_ID);
          String parameterValue2 = (String)context.getOptionalValue(PARAMETER2_ID);
          
          //do the processing
          newObjectId = ...
          //...
          
          //enable rollback
          context.putValue(PARAMETER3_ID, newObjectId);

          //return result
          IExtensionResult result = 
                 extensionFactory.createExtensionResult(IExtensionResult.OK);
          result.putValue(PARAMETER3_ID, newObjectId);
          return result;
     }

     //ON_DELETE_ROOM
     //...

     return null;
}

The sample code shows how the process method works. Dependent on the extension point, first the input parameters are drawn from the context, and then the output parameters are processed and added to the result. It is important, that the extension should throw the ExtensionException only, if the execution would cause irreversible damage to the room. If it throws an exception, this causes that a user action cannot be performed, e.g. a room cannot be created, a room part cannot be added or a user cannot be invited to the room. If a minor problem occurs that should not stop the user action, the application must handle it by itself. For example, it could copy a document to its workspace that describes how the problem can be healed manually.

The validate-, rollback- and commit methods are implemented in a similar way, no extra sample code is shown here.

How to develop a Value Set Provider?

A value set provider provides a set of allowed values for a dedicated parameter. This set can be used to show drop down list boxes in the UI or to validate a user input. Figure 8 shows the template wizard with an opened value set, which lists initial content that can be used to fill the workspace of an iView, when a room or room part is created.


Figure 8: Template Wizard with opened Value Set

Like extensions, value set providers are portal services, and they are also displayed in the template wizards. Because of this, the first two steps to develop a value set provider are identical. First, ids are provided (see Error! Reference source not found.) and second, the same methods are implemented to make the value set provider displayable (see 2.2).

List the value sets

The value set provider defines a list of parameters, for which it exposes value sets. The sample source shows that the parameter list is build the same way as in 2.5.

public IParameterInfo[] getParameters() {
     IParameterInfo[] result = {
          createParameterInfo(PARAMETER1_ID, PARAMETER1_CLASS),
          createParameterInfo(PARAMETER2_ID, PARAMETER2_CLASS),
          //...
     };
     return result;
 }

List the required parameters

To compute the value set of a parameter, the provider may require some input parameters. For example, if the value set should be the days of a month, it would require the month and the year to calculate the output. For each parameter in 3.1, the value set lists the required parameters. The parameter construction works the same way as in 2.5.

public IParameterInfo[] getRequiredParameters(IParameterInfo parameter) {
     IParameterInfo[] result = null;
          
     //PARAMETER1
     //no input required

     //PARAMETER2
     if (parameter2.getId().equals(parameter.getId())) {
          IParameterInfo[] result = { 
                 createParameterInfo(PARAMETER3_ID, PARAMETER3_CLASS),
                 //...
          };
          return result;
     }

     //PARAMETER3
     //...
          
     return null;
}

Please note that in the current version of the rooms this feature is not supported by the UI, so that it cannot be used. You will just code the last line to return null.

Make the value set provider executable

The value set provider computes the value set for each parameter listed in 3.1. Like the extension, it takes the required input parameters defined in 3.2 from a context.

public IValueSet getValueSet(IParameterInfo parameter, IExtensionContext context) throws ValueSetProviderException
{
     //PARAMETER1
     if (parameter1.getId().equals(parameter.getId())) {
          
          //get input parameters
          String parameterValue2 = (String)context.getValue(PARAMETER2_ID);
          
          //compute the value set
          IDisplayable value1Displayable = extensionFactory.createDisplayable(
                 VALUE_ID1, RESOURCE_BUNDLE, "lbl_" + VALUE_ID1, 
                 "dsc_" + VALUE_ID1);
          Object value1 = getValue(parameterValue2);
          ...
          IParameterValue[] result = {
                 extensionFactory.createParameterValue(value1Displayable, value1),
                 extensionFactory.createParameterValue(value2Displayable, value2),
                 //...
          };
          return extensionFactory.createValueSet(result, exclusive);
          
     }

     //PARAMETER2
     ...

     return null;
}


Extension Points and Context

Extension Point

Predecessor

System Parameter Ids

A

ON_CREATE_ROOM

ROOM_INFO

B

ON_ADD_ROOMROLE_TO_ROOM

A

ROOM_INFO ADDED_ROOMROLE_TO_ROOM

C

ON_ADD_USERS_TO_ROOMROLES

A, B

ROOM_INFO ADDED_USERS_TO_ROOMROLES

D

ON_SET_OWNER_OF_ROOM

A

ROOM_INFO OLD_OWNER_OF_ROOM NEW_OWNER_OF_ROOM

E

ON_AFTER_CREATE_ROOM

A, B, C, D

ROOM_INFO

F

ON_CREATE_ROOMPART

ROOMPART_ID ROOMPART_TEMPLATE_ID ROOMPART_INFO

G

ON_ADD_ROOMPART_TO_ROOM

A, B, C, D, E, F

ROOM_INFO ROOMPART_INFO

H

ON_REMOVE_ROOMPART_FROM_ROOM

A, B, C, D, E, F, G

ROOM_INFO

ROOMPART_INFO

I

ON_DELETE_ROOMPART

F

ROOMPART_ID ROOMPART_TEMPLATE_ID ROOMPART_INFO

J

ON_BEFORE_DELETE_ROOM

A, B, C, D, E

ROOM_INFO

K

ON_REMOVE_USERS_FROM_ROOMROLES

A, B, C

ROOM_INFO REMOVED_USERS_FROM_ROOMROLES

L

ON_REMOVE_ROOMROLE_FROM_ROOM

A, B

ROOM_INFO REMOVED_ROOMROLE_FROM_ROOM

M

ON_DELETE_ROOM

A, B, C, D, E, J, K, L

ROOM_INFO

N

ON_LOCK_ROOM

A, B, C, D, E

ROOM_INFO

O

ON_UNLOCK_ROOM

A, B, C, D, E, N

ROOM_INFO

P

ON_HIDE_ROOM

A, B, C, D, E

ROOM_INFO

Q

ON_UNHIDE_ROOM

A, B, C, D, E, P

ROOM_INFO

R

ON_ ON_SET_OWNERFULLACCESS_IN_ROOM

A, B, C, D, E

ROOM_INFO

S

ON_REMOVE_ OWNERFULLACCESS_IN_ROOM

A, B, C, D, E, R

ROOM_INFO

Table 1: Extension points and system parameters added to the context

List of Extensions provided by SAP

Id

Category

Description

cmRoomExtensionSP2FP

room

Creates, handles and deletes CM workspaces for rooms and room parts. Also organizes the entry points to allow copy and move activities between the workspaces of a room.

roomPropertyReader

room

Exposes properties from the system parameter ROOM_INFO.

roompartPropertyReader

room

Exposes properties from the system parameter ROOMPART_INFO.

concat

common

Concatenates two strings.

configReader

common

Exposes properties of configurables in the Configuration Framework


FAQ

The extension does not show up in the template wizard.

An input parameter always has the same value.

Since an extension is a service, there is only one instance in the portal. It is a singleton. This means if you use the instance to cache an input parameter, it stays unchanged until the portal restarts.

//don't cache context parameters like this:
if (this.myParameter == null) {
     this.myParameter = context.getValue(MY_PARAMETER_ID);
}

I cannot retrieve a system parameter from the context.

You probably forgot to declare the parameter in the getInputParameterInfos() method of your extension. Only parameters declared in that method will be available, this is also true for system parameters.

I declared a system parameter, but when the extension runs, it says "Parameter is missing"

The system parameter may not be available at each extension point. Check in Table 1, which parameters are available at which points.

The extension cannot find the resource bundles.

You probably did not activate the generic class loader registration in the portalapp.xml file like this:

<property name="generic_classloader_registration" value="yes"/>

It is sufficient to do the activation once, which means for one service in the portalapp.xml.

Glossary

Context

Set of parameter values that belong to a room or room part. The parameters may be constants defined by the template creator at design time, external parameters prompted from the user at run time or extension output processed at run time.

Design time

The design time denotes the creation of a room- or room part template.

Extension

Portal service that ads functionality to SAP Collaboration Room. It defines input and output parameters, which are mapped at design time and processed at run time, when dedicated extension points are reached. Extensions are similar to plug ins or customer exits.

Extension Point

Step in the life cycle of a SAP Collaboration Room at which extensions are processed.

Parameter Mapping

Step at design time, where the template creator assigns extension parameters to parameters of the room or room part context.

Run time

The runtime encloses the life cycle of a room that begins with the creation and ends with the deletion.

Template

PCD object that contains required information to create a room or a room part. The template is created by the template creator at design time and used by the room creator at run time.

Value Set Provider

Portal service that provides value sets for parameter mapping.